const log=require('electron-log/main')

// Optional, initialize the logger for any renderer process
log.initialize();

log.info('Log from the main process');
let safeExit = false;
const pwd=require("./pwd")
// var languages={}
/* provides helper functions for using localized strings */

/*
translations are compiled into here by running "npm run build" in this format

var languages = {
    en-US: {name: "English (United States), identifier: "en-US", translations: {...}}
}

*/

function getCurrentLanguage () {
  // TODO add a setting to change the language to something other than the default

  var language = 'en-US' // default

  if (typeof navigator !== 'undefined') { // renderer process
    language = navigator.language
  } else if (typeof app !== 'undefined') { // main process
    language = app.getLocale()
  } else {
    // nothing worked, fall back to default
  }

  return language
}

var userLanguage = null

function l (stringId) {
	return stringId
  // if (!userLanguage) {
  //   userLanguage = getCurrentLanguage()
  // }

  // var userBaseLanguage = userLanguage.split('-')[0] // examples: es-419 -> es, nl-BE -> nl

  // // get the translated string for the given ID

  // // try an exact match for the user language
  // if (languages[userLanguage] && languages[userLanguage].translations[stringId] && languages[userLanguage].translations[stringId].unsafeHTML !== null) {
  //   return languages[userLanguage].translations[stringId]
  //   // try a match for the base language, if the language code is for a particular region
  // } else if (languages[userBaseLanguage] && languages[userBaseLanguage].translations[stringId] && languages[userBaseLanguage].translations[stringId].unsafeHTML !== null) {
  //   return languages[userBaseLanguage].translations[stringId]
  // } else {
  //   // fallback to en-US
  //   return languages['en-US'].translations[stringId]
  // }
}

/* for static HTML pages
insert a localized string into all elements with a [data-string] attribute
set the correct attributes for all elements with a [data-label] attribute
set the value attribute for all elements with a [data-value] attribute
 */

if (typeof document !== 'undefined') {
  document.querySelectorAll('[data-string]').forEach(function (el) {
    var str = l(el.getAttribute('data-string'))
    if (typeof str === 'string') {
      el.textContent = str
    } else if (str && str.unsafeHTML && el.hasAttribute('data-allowHTML')) {
      el.innerHTML = str.unsafeHTML
    }
  })
  document.querySelectorAll('[data-label]').forEach(function (el) {
    var str = l(el.getAttribute('data-label'))
    if (typeof str === 'string') {
      el.setAttribute('title', str)
      el.setAttribute('aria-label', str)
    } else {
      throw new Error('invalid data-label value: ' + str)
    }
  })
  document.querySelectorAll('[data-value]').forEach(function (el) {
    var str = l(el.getAttribute('data-value'))
    if (typeof str === 'string') {
      el.setAttribute('value', str)
    } else {
      throw new Error('invalid data-value value: ' + str)
    }
  })

}
if (typeof window !== 'undefined') {
  window.l = l
  window.userLanguage = userLanguage
  window.getCurrentLanguage = getCurrentLanguage
}
;
function buildAppMenu (options = {}) {
  var tabTaskActions = [
    {
      label: l('NewTab'),
      accelerator: 'CmdOrCtrl+t',
      click: function (item, window, event) {
        // keyboard shortcuts for these items are handled in the renderer
        if (!event.triggeredByAccelerator) {
          sendIPCToWindow(window, 'addTab')
        }
      }
    },
    // {
    //   label: l('DuplicateTab'),
    //   accelerator: 'shift+CmdOrCtrl+d',
    //   click: function (item, window, event) {
    //     if (!event.triggeredByAccelerator) {
    //       sendIPCToWindow(window, 'duplicateTab')
    //     }
    //   }
    // },
    // {
    //   label: l('NewPrivateTab'),
    //   accelerator: 'shift+CmdOrCtrl+p',
    //   click: function (item, window, event) {
    //     if (!event.triggeredByAccelerator) {
    //       sendIPCToWindow(window, 'addPrivateTab')
    //     }
    //   }
    // },
    // {
    //   label: l('NewTask'),
    //   accelerator: 'CmdOrCtrl+n',
    //   click: function (item, window, event) {
    //     if (!event.triggeredByAccelerator) {
    //       sendIPCToWindow(window, 'addTask')
    //     }
    //   }
    // }
  ]

  var personalDataItems = [
    {
      label: l('Bookmarks'),
      accelerator: undefined,
      click: function (item, window) {
        sendIPCToWindow(window, 'showBookmarks')
      }
    },
    {
      label: l('History'),
      accelerator: undefined,
      click: function (item, window) {
        sendIPCToWindow(window, 'showHistory')
      }
    }
  ]

  var quitAction = {
    label: l('Quit').replace('%n', app.name),
    accelerator: 'CmdOrCtrl+Q',
    click: function () {
      app.quit()
    }
  }

  var template = [
    ...(options.secondary ? tabTaskActions : []),
    ...(options.secondary ? [{ type: 'separator' }] : []),
    ...(options.secondary ? personalDataItems : []),
    ...(options.secondary ? [{ type: 'separator' }] : []),
    ...(process.platform === 'darwin'
    ? [
        {
          label: app.name,
          submenu: [
            {
              label: l('About').replace('%n', app.name),
              role: 'about'
            },
            {
              type: 'separator'
            },
            {
              label: l('Preferences'),
              accelerator: 'CmdOrCtrl+,',
              click: function (item, window) {
                sendIPCToWindow(window, 'addTab', {
                  url: 'file://' + __dirname + '/pages/settings/index.html'
                })
              }
            },
            {
              label: 'Services',
              role: 'services',
              submenu: []
            },
            {
              type: 'separator'
            },
            {
              label: l('Hide').replace('%n', app.name),
              accelerator: 'CmdOrCtrl+H',
              role: 'hide'
            },
            {
              label: l('HideOthers'),
              accelerator: 'CmdOrCtrl+Shift+H',
              role: 'hideothers'
            },
            {
              label: l('ShowAll'),
              role: 'unhide'
            },
            {
              type: 'separator'
            },
            quitAction
          ]
        }
      ] : []),
    {
      label: l('File'),
      submenu: [
        ...(!options.secondary ? tabTaskActions : []),
        ...(!options.secondary ? [{ type: 'separator' }] : []),
        {
          label: l('SavePageAs'),
          accelerator: 'CmdOrCtrl+s',
          click: function (item, window) {
            sendIPCToWindow(window, 'saveCurrentPage')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('Print'),
          accelerator: 'CmdOrCtrl+p',
          click: function (item, window) {
            sendIPCToWindow(window, 'print')
          }
        },
        ...( [{ type: 'separator' }] ),
        ...([quitAction])
      ]
    },
    {
      label: l('Edit'),
      submenu: [
        {
          label: l('Undo'),
          accelerator: 'CmdOrCtrl+Z',
          role: 'undo'
        },
        {
          label: l('Redo'),
          accelerator: 'Shift+CmdOrCtrl+Z',
          role: 'redo'
        },
        {
          type: 'separator'
        },
        {
          label: l('Cut'),
          accelerator: 'CmdOrCtrl+X',
          role: 'cut'
        },
        {
          label: l('Copy'),
          accelerator: 'CmdOrCtrl+C',
          role: 'copy'
        },
        {
          label: l('Paste'),
          accelerator: 'CmdOrCtrl+V',
          role: 'paste'
        },
        {
          label: l('SelectAll'),
          accelerator: 'CmdOrCtrl+A',
          role: 'selectall'
        },
        {
          type: 'separator'
        },
        {
          label: l('Find'),
          accelerator: 'CmdOrCtrl+F',
          click: function (item, window) {
            sendIPCToWindow(window, 'findInPage')
          }
        },
        ,
        {
          label: 'download',
          accelerator: 'CmdOrCtrl+D',
          click: function (item, window) {
            sendIPCToWindow(window, 'download')
          }
        }
        ,
        {
          label: 'password',
          accelerator: 'CmdOrCtrl+P',
          click: function (item, window) {
            sendIPCToWindow(window, 'password')
          }
        },
        ...(process.platform !== 'darwin' ? [{ type: 'separator' }] : []),
        ...(process.platform !== 'darwin' ? [{
          label: l('Preferences'),
          accelerator: 'CmdOrCtrl+,',
          click: function (item, window) {
          	sendIPCToWindow(window, 'preferences')
            // sendIPCToWindow(window, 'addTab', {
            //   url: 'file://' + __dirname + '/pages/settings/index.html'
            // })
          }
        }] : [])
      ]
    },
    {
      label: l('View'),
      submenu: [
        ...(!options.secondary ? personalDataItems : []),
        ...(!options.secondary ? [{ type: 'separator' }] : []),
        {
          label: 'mainMenu show',
          click: function (item, window) {
            // console.log("mainMenu show");
            window.setMenuBarVisibility(true);
            // mainWindow.setMenuBarVisibility(true);
          }
        },
        {
          label: 'mainMenu hide',
          click: function (item, window) {
            window.setMenuBarVisibility(false);
          }
        },

        {
          label: l('ZoomIn'),
          accelerator: 'CmdOrCtrl+Plus',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomIn')
          }
        },
        {
          label: l('ZoomOut'),
          accelerator: 'CmdOrCtrl+-',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomOut')
          }
        },
        {
          label: l('ActualSize'),
          accelerator: 'CmdOrCtrl+0',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomReset')
          }
        },
        {
          type: 'separator'
        },
        // {
        //   label: l('FocusMode'),
        //   accelerator: undefined,
        //   type: 'checkbox',
        //   checked: false,
        //   click: function (item, window) {
        //     if (isFocusMode) {
        //       isFocusMode = false
        //       sendIPCToWindow(window, 'exitFocusMode')
        //     } else {
        //       isFocusMode = true
        //       sendIPCToWindow(window, 'enterFocusMode')
        //     }
        //   }
        // },
        {
          label: l('FullScreen'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              {return 'Ctrl+Command+F'}
            else
              {return 'F11'}
          })(),
          role: 'togglefullscreen'
        }
      ]
    },
    {
      label: l('Developer'),
      submenu: [
        {
          label: l('InspectPage'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              {return 'Cmd+Alt+I'}
            else
              {return 'Ctrl+Shift+I'}
          })(),
          click: function (item, window) {
            sendIPCToWindow(window, 'inspectPage')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('ReloadBrowser'),
          accelerator: undefined,
          click: function (item, focusedWindow) {
            if (focusedWindow) {
              destroyAllViews()
              focusedWindow.reload()
            }
          }
        },
        {
          label: l('InspectBrowser'),
          click: function (item, focusedWindow) {
         	console.log("appMenuInspectBrowser")
            if (focusedWindow){ 
            	console.log("focusedWindow");
            	focusedWindow.toggleDevTools()
        	}
        	else{
        		console.log("no focusedWindow")
        	}
          }
        }
      ]
    },
    ...(process.platform === 'darwin' ? [
      {
        label: l('Window'),
        role: 'window',
        submenu: [
          {
            label: l('Minimize'),
            accelerator: 'CmdOrCtrl+M',
            role: 'minimize'
          },
          {
            label: l('Close'),
            accelerator: 'CmdOrCtrl+W',
            click: function (item, window) {
              if (mainWindow && !mainWindow.isFocused()) {
                // a devtools window is focused, close it
                var contents = webContents.getAllWebContents()
                for (var i = 0; i < contents.length; i++) {
                  if (contents[i].isDevToolsFocused()) {
                    contents[i].closeDevTools()
                    return
                  }
                }
              }
            // otherwise, this event will be handled in the main window
            }
          },
          {
            label: l('AlwaysOnTop'),
            type: 'checkbox',
            checked: settings.get('windowAlwaysOnTop') || false,
            click: function (item, window) {
              if (mainWindow) {
                mainWindow.setAlwaysOnTop(item.checked)
              }
              settings.set('windowAlwaysOnTop', item.checked)
            }
          },
          {
            type: 'separator'
          },
          {
            label: l('BringToFront'),
            role: 'front'
          }
        ]
      }
    ] : []),
    {
      label: l('Help'),
      role: 'help',
      submenu: [
        {
          label: l('KeyboardShortcuts'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/wiki#keyboard-shortcuts')
          }
        },
        {
          label: l('ReportBug'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/issues/new')
          }
        },
        {
          label: l('TakeTour'),
          click: function () {
            openTabInWindow('https://minbrowser.github.io/min/tour/')
          }
        },
        {
          label: l('ViewGithub'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min')
          }
        },
        ...(process.platform !== 'darwin' ? [{ type: 'separator' }] : []),
        ...(process.platform !== 'darwin' ? [{
          label: l('About').replace('%n', app.name),
          click: function (item, window) {
            var info = [
              'Min v' + app.getVersion(),
              'Chromium v' + process.versions.chrome
            ]
            sendIPCToWindow(window, 'about_click',info)
            electron.dialog.showMessageBox({
              type: 'info',
              title: l('About').replace('%n', app.name),
              message: info.join('\n'),
              buttons: [l('closeDialog')]
            })
          }
        }] : [])
      ]
    }
  ]
  return Menu.buildFromTemplate(template)
}

function createDockMenu () {
  // create the menu. based on example from https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#custom-dock-menu-macos
  if (process.platform === 'darwin') {
    var Menu = electron.Menu

    var template = [
      {
        label: l('NewTab'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addTab')
        }
      },
      // {
      //   label: l('NewPrivateTab'),
      //   click: function (item, window) {
      //     sendIPCToWindow(window, 'addPrivateTab')
      //   }
      // },
      // {
      //   label: l('NewTask'),
      //   click: function (item, window) {
      //     sendIPCToWindow(window, 'addTask')
      //   }
      // }
    ]

    var dockMenu = Menu.buildFromTemplate(template)
    app.dock.setMenu(dockMenu)
  }
}
;
const TouchBar = require('electron').TouchBar
const nativeImage = require('electron').nativeImage
const { TouchBarLabel, TouchBarButton, TouchBarSpacer } = TouchBar

function buildTouchBar () {
  if (process.platform !== 'darwin') {
    return null
  }

  return new TouchBar({
    items: [
      new TouchBarButton({
        accessibilityLabel: l('goBack'),
        icon: nativeImage.createFromNamedImage('NSImageNameTouchBarGoBackTemplate', [-1, 0, 1]),
        click: function () {
          sendIPCToWindow(mainWindow, 'goBack')
        }
      }),
      new TouchBarSpacer({ size: 'flexible' }),
      new TouchBarButton({
        icon: nativeImage.createFromNamedImage('NSImageNameTouchBarSearchTemplate', [-1, 0, 1]),
        iconPosition: 'left',
        // TODO this is really hacky, find a better way to set the size
        label: '    ' + l('searchbarPlaceholder') + '                                ',
        click: function () {
          sendIPCToWindow(mainWindow, 'openEditor')
        }
      }),
      new TouchBarSpacer({ size: 'flexible' }),
      new TouchBarButton({
        icon: nativeImage.createFromNamedImage('NSImageNameTouchBarAdd', [-1, 0, 1]),
        accessibilityLabel: l('newTabAction'),
        click: function () {
          sendIPCToWindow(mainWindow, 'addTab')
        }
      }),
      new TouchBarButton({
        accessibilityLabel: l('viewTasks'),
        icon: nativeImage.createFromNamedImage('NSImageNameTouchBarListViewTemplate', [-1, 0, 1]),
        click: function () {
          sendIPCToWindow(mainWindow, 'toggleTaskOverlay')
        }
      })
    ]
  })
}
;
var regedit = require('regedit')

var installPath = process.execPath

var keysToCreate = [
  'HKCU\\Software\\Classes\\Min',
  'HKCU\\Software\\Classes\\Min\\Application',
  'HKCU\\Software\\Classes\\Min\\DefaulIcon',
  'HKCU\\Software\\Classes\\Min\\shell\\open\\command',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\FileAssociations',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\StartMenu',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\URLAssociations',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\DefaultIcon',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\InstallInfo',
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\shell\\open\\command'
]

var registryConfig = {
  'HKCU\\Software\\RegisteredApplications': {
    'Min': {
      value: 'Software\\Clients\\StartMenuInternet\\Min\\Capabilities',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Classes\\Min': {
    'default': {
      value: 'Min Browser Document',
      type: 'REG_DEFAULT'
    }
  },
  'HKCU\\Software\\Classes\\Min\\Application': {
    'ApplicationIcon': {
      value: installPath + ',0',
      type: 'REG_SZ'
    },
    'ApplicationName': {
      value: 'Min',
      type: 'REG_SZ'
    },
    'AppUserModelId': {
      value: 'Min',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Classes\\Min\\DefaulIcon': {
    'ApplicationIcon': {
      value: installPath + ',0',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Classes\\Min\\shell\\open\\command': {
    'default': {
      value: '"' + installPath + '" "%1"',
      type: 'REG_DEFAULT'
    }
  },
  'HKCU\\Software\\Classes\\.htm\\OpenWithProgIds': {
    'Min': {
      value: 'Empty',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Classes\\.html\\OpenWithProgIds': {
    'Min': {
      value: 'Empty',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\FileAssociations': {
    '.htm': {
      value: 'Min',
      type: 'REG_SZ'
    },
    '.html': {
      value: 'Min',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\StartMenu': {
    'StartMenuInternet': {
      value: 'Min',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\Capabilities\\URLAssociations': {
    'http': {
      value: 'Min',
      type: 'REG_SZ'
    },
    'https': {
      value: 'Min',
      type: 'REG_SZ'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\DefaultIcon': {
    'default': {
      value: installPath + ',0',
      type: 'REG_DEFAULT'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\InstallInfo': {
    'IconsVisible': {
      value: 1,
      type: 'REG_DWORD'
    }
  },
  'HKCU\\Software\\Clients\\StartMenuInternet\\Min\\shell\\open\\command': {
    'default': {
      value: installPath,
      type: 'REG_DEFAULT'
    }
  }
}

var registryInstaller = {
  install: function () {
    return new Promise(function (resolve, reject) {
      regedit.createKey(keysToCreate, function (err) {
        regedit.putValue(registryConfig, function (err) {
          if (err) {
            reject()
          } else {
            resolve()
          }
        })
      })
    })
  },
  uninstall: function () {
    return new Promise(function (resolve, reject) {
      regedit.deleteKey(keysToCreate, function (err) {
        if (err) {
          reject()
        } else {
          resolve()
        }
      })
    })
  }
};
const electron = require('electron')
const fs = require('fs')
const path = require('path')
const app = electron.app // Module to control application life.
const protocol = electron.protocol // Module to control protocol handling
const BrowserWindow = electron.BrowserWindow // Module to create native browser window.
const webContents = electron.webContents
const session = electron.session
const ipc = electron.ipcMain
// const { BrowserWindow, dialog, ipcMain } = require('electron')
// const path = require('path')
//...
async function handleFileOpen() {
  const { canceled, filePaths } = await dialog.showOpenDialog()
  if (canceled) {
    return
  } else {
    return filePaths[0]
  }
}

ipc.on('quit', function (e) {
	app.quit();
})

ipc.handle('minimize', function (e) {
  mainWindow.minimize()
  // workaround for https://github.com/minbrowser/min/issues/1662
  mainWindow.webContents.send('minimize')
})

ipc.handle('maximize', function (e) {
  mainWindow.maximize()
  // workaround for https://github.com/minbrowser/min/issues/1662
  mainWindow.webContents.send('maximize')
})

ipc.handle('unmaximize', function (e) {
  mainWindow.unmaximize()
  // workaround for https://github.com/minbrowser/min/issues/1662
  mainWindow.webContents.send('unmaximize')
})

ipc.handle('close', function (e) {
  mainWindow.close()
})

ipc.handle('setFullScreen', function (e, fullScreen) {
  mainWindow.setFullScreen(e, fullScreen)
})
ipc.handle('hideMenuBar', function (e, fullScreen) {
  mainWindow.setMenuBarVisibility(e, fullScreen)
})
ipc.on('home_path', (event, arg) => {
    event.returnValue = __dirname;
})
ipc.handle('showOpenDialog', async function (e, options) {
    const result = await dialog.showOpenDialog(mainWindow, options)
    return result.filePaths
})

ipc.handle('showSaveDialog', async function (e, options) {
  const result = await dialog.showSaveDialog(mainWindow, options)
  return result.filePath
})
// ipc.on('savedlg', (event, arg) => {
//   console.log(arg) // prints "ping"
//   const { dialog } = require('electron')
//   dialog.showSaveDialog({ 
//     defaultPath:arg.defaultPath,
//     filters:arg.filters,
//   }).then(
//     (res)=>{
//       console.log(res);
//       event.reply('savedlg_res', res);
//   });
// })
// ipc.on('opendlg', (event, arg) => {
//   console.log(arg) // prints "ping"
//   const { dialog } = require('electron')
//   dialog.showOpenDialog({ 
//     defaultPath:arg.defaultPath,
//     filters:arg.filters,
//     properties: ['openFile', 'multiSelections'] 
//   }).then(
//     (res)=>{
//       console.log(res);
//       event.reply('opendlg_res', res);
//   });
// })
// const Menu = electron.Menu
// const MenuItem = electron.MenuItem
const {Menu, MenuItem, dialog, ipcMain }=electron;
if (process.argv.some(arg => arg === '-v' || arg === '--version')) {
  // console.log('Min: ' + app.getVersion())
  // console.log('Chromium: ' + process.versions.chrome)
  process.exit()
}

let isInstallerRunning = false
const isDevelopmentMode = process.argv.some(arg => arg === '--development-mode')

function clamp (n, min, max) {
  return Math.max(Math.min(n, max), min)
}

if (process.platform === 'win32') {
  // (async function () {
  //   var squirrelCommand = process.argv[1]
  //   if (squirrelCommand === '--squirrel-install' || squirrelCommand === '--squirrel-updated') {
  //     isInstallerRunning = true
  //     await registryInstaller.install()
  //   }
  //   if (squirrelCommand === '--squirrel-uninstall') {
  //     isInstallerRunning = true
  //     await registryInstaller.uninstall()
  //   }
  //   if (require('electron-squirrel-startup')) {
  //     app.quit()
  //   }
  // })()
}

// app.setPath('userData', __dirname)

// workaround for flicker when focusing app (https://github.com/electron/electron/issues/17942)
app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true')

var userDataPath =app.getPath('userData')
console.log(userDataPath)

const browserPage = 'file://' + __dirname + '/index.html'
var mainWindow = null
var mainWindowIsMinimized = false // workaround for https://github.com/minbrowser/min/issues/1074
var mainMenu = null
var secondaryMenu = null
var isFocusMode = false
var appIsReady = false

const isFirstInstance = app.requestSingleInstanceLock()

if (!isFirstInstance) {
  app.quit()
  return
}

var saveWindowBounds = function () {
  if (mainWindow) {
    var bounds = Object.assign(mainWindow.getBounds(), {
      maximized: mainWindow.isMaximized()
    })
    fs.writeFileSync(path.join(userDataPath, 'windowBounds.json'), JSON.stringify(bounds))
  }
}

function sendIPCToWindow (window, action, data) {
  if (window && window.webContents && window.webContents.isLoadingMainFrame()) {
    // immediately after a did-finish-load event, isLoading can still be true,
    // so wait a bit to confirm that the page is really loading
    setTimeout(function() {
      if (window.webContents.isLoadingMainFrame()) {
        window.webContents.once('did-finish-load', function () {
          window.webContents.send(action, data || {})
        })
      } else {
        window.webContents.send(action, data || {})
      }
    }, 0)
  } else if (window) {
    window.webContents.send(action, data || {})
  } else {
    var window = createWindow()
    window.webContents.once('did-finish-load', function () {
      window.webContents.send(action, data || {})
    })
  }
}

function openTabInWindow (url) {
  sendIPCToWindow(mainWindow, 'addTab', {
    url: url
  })
}

function handleCommandLineArguments (argv) {
  // the "ready" event must occur before this function can be used
  if (argv) {
    argv.forEach(function (arg, idx) {
      if (arg && arg.toLowerCase() !== __dirname.toLowerCase()) {
        // URL
        if (arg.indexOf('://') !== -1) {
          sendIPCToWindow(mainWindow, 'addTab', {
            url: arg
          })
        } else if (idx > 0 && argv[idx - 1] === '-s') {
          // search
          sendIPCToWindow(mainWindow, 'addTab', {
            url: arg
          })
        } else if (/\.(m?ht(ml)?|pdf)$/.test(arg) && fs.existsSync(arg)) {
          // local files (.html, .mht, mhtml, .pdf)
          sendIPCToWindow(mainWindow, 'addTab', {
            url: 'file://' + path.resolve(arg)
          })
        }
      }
    })
  }
}

function createWindow () {
  var bounds;

  try {
    var data = fs.readFileSync(path.join(userDataPath, 'windowBounds.json'), 'utf-8')
    bounds = JSON.parse(data)
  } catch (e) {}

  if (!bounds) { // there was an error, probably because the file doesn't exist
    var size = electron.screen.getPrimaryDisplay().workAreaSize
    bounds = {
      x: 0,
      y: 0,
      width: size.width,
      height: size.height,
      maximized: true
    }
  }

  // make the bounds fit inside a currently-active screen
  // (since the screen Min was previously open on could have been removed)
  // see: https://github.com/minbrowser/min/issues/904
  var containingRect = electron.screen.getDisplayMatching(bounds).workArea

  bounds = {
    x: clamp(bounds.x, containingRect.x, (containingRect.x + containingRect.width) - bounds.width),
    y: clamp(bounds.y, containingRect.y, (containingRect.y + containingRect.height) - bounds.height),
    width: clamp(bounds.width, 0, containingRect.width),
    height: clamp(bounds.height, 0, containingRect.height),
    maximized: bounds.maximized
  }

  return createWindowWithBounds(bounds)
}

function createWindowWithBounds (bounds) {


  mainWindow = new BrowserWindow({
    width: bounds.width,
    height: bounds.height,
    x: bounds.x,
    y: bounds.y,
    minWidth: (process.platform === 'win32' ? 400 : 320), // controls take up more horizontal space on Windows
    minHeight: 350,
    titleBarStyle: 'hidden',
    // trafficLightPosition: { x: 12, y: 19 },
    icon: __dirname + '/icons/icon256.png',
    // frame: process.platform === 'darwin' || settings.get('useSeparateTitlebar') === true,
    // alwaysOnTop: settings.get('windowAlwaysOnTop'),
    // backgroundColor: '#fff', // the value of this is ignored, but setting it seems to work around https://github.com/electron/electron/issues/10559
    webPreferences: {
      devTools:true,
      nodeIntegration: true,
      contextIsolation: false,
      enableRemoteModule: true,
      nodeIntegrationInWorker: true, // used by ProcessSpawner
      additionalArguments: [
        '--user-data-path=' + userDataPath,
        '--app-version=' + app.getVersion(),
        ...((isDevelopmentMode ? ['--development-mode'] : []))
      ]
    }
  })
  // windows and linux always use a menu button in the upper-left corner instead
  // if frame: false is set, this won't have any effect, but it does apply on Linux if "use separate titlebar" is enabled
  // if (process.platform !== 'darwin') {
  // mainWindow.setMenuBarVisibility(true) //false)
  // }

  // and load the index.html of the app.
  mainWindow.loadURL(browserPage)
  // console.log("webContents:",mainWindow.webContents)
  mainWindow.webContents.openDevTools();
  // mainWindow.openDevTools();

  if (bounds.maximized) {
    mainWindow.maximize()

    mainWindow.webContents.on('did-finish-load', function () {
      sendIPCToWindow(mainWindow, 'maximize')
    })
  }
  mainWindow.on('close', function (e) {
  	if(!safeExit){
      e.preventDefault();
      mainWindow.webContents.send('request_close');
    }else{
	  // destroyAllViews()
	  // save the window size for the next launch of the app
	  saveWindowBounds()
	}
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
    mainWindowIsMinimized = false
  })
  mainWindow.on('focus', function () {
    if (!mainWindowIsMinimized) {
      sendIPCToWindow(mainWindow, 'windowFocus')
    }
  })

  mainWindow.on('minimize', function () {
    sendIPCToWindow(mainWindow, 'minimize')
    mainWindowIsMinimized = true
  })

  mainWindow.on('restore', function () {
    mainWindowIsMinimized = false
  })

  mainWindow.on('maximize', function () {
    sendIPCToWindow(mainWindow, 'maximize')
  })

  mainWindow.on('unmaximize', function () {
    sendIPCToWindow(mainWindow, 'unmaximize')
  })

  mainWindow.on('enter-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-full-screen')
  })

  mainWindow.on('leave-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-full-screen')
    // https://github.com/minbrowser/min/issues/1093
    // mainWindow.setMenuBarVisibility(false)
  })

  mainWindow.on('enter-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-html-full-screen')
  })

  mainWindow.on('leave-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-html-full-screen')
  })

  /*
  Handles events from mouse buttons
  Unsupported on macOS, and on Linux, there is a default handler already,
  so registering a handler causes events to happen twice.
  See: https://github.com/electron/electron/issues/18322
  */
  if (process.platform === 'win32') {
    mainWindow.on('app-command', function (e, command) {
      if (command === 'browser-backward') {
        sendIPCToWindow(mainWindow, 'goBack')
      } else if (command === 'browser-forward') {
        sendIPCToWindow(mainWindow, 'goForward')
      }
    })
  }

  // prevent remote pages from being loaded using drag-and-drop, since they would have node access
  mainWindow.webContents.on('will-navigate', function (e, url) {
    if (url !== browserPage) {
      e.preventDefault()
    }
  })
  // return mainWindow

  // mainWindow.setTouchBar(buildTouchBar())

  return mainWindow
}

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
  appIsReady = true
  ipc.handle('dialog:openFile', handleFileOpen)
  /* the installer launches the app to install registry items and shortcuts,
  but if that's happening, we shouldn't display anything */
  if (isInstallerRunning) {
    return
  }

  createWindow()

  mainWindow.webContents.on('did-finish-load', function () {
    // if a URL was passed as a command line argument (probably because Min is set as the default browser on Linux), open it.
    handleCommandLineArguments(process.argv)

    // there is a URL from an "open-url" event (on Mac)
    if (global.URLToOpen) {
      // if there is a previously set URL to open (probably from opening a link on macOS), open it
      sendIPCToWindow(mainWindow, 'addTab', {
        url: global.URLToOpen
      })
      global.URLToOpen = null
    }
  })

  mainMenu = buildAppMenu()
  Menu.setApplicationMenu(mainMenu)
  createDockMenu()
})
app.on('open-url', function (e, url) {
  if (appIsReady) {
    sendIPCToWindow(mainWindow, 'addTab', {
      url: url
    })
  } else {
    global.URLToOpen = url // this will be handled later in the createWindow callback
  }
})

// handoff support for macOS
app.on('continue-activity', function(e, type, userInfo, details) {
  if (type === 'NSUserActivityTypeBrowsingWeb' && details.webpageURL) {
    e.preventDefault()
    sendIPCToWindow(mainWindow, 'addTab', {
      url: details.webpageURL
    })
  }
})

app.on('second-instance', function (e, argv, workingDir) {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }
    mainWindow.focus()
    // add a tab with the new URL
    handleCommandLineArguments(argv)
  }
})

/**
 * Emitted when the application is activated, which usually happens when clicks on the applications's dock icon
 * https://github.com/electron/electron/blob/master/docs/api/app.md#event-activate-os-x
 *
 * Opens a new tab when all tabs are closed, and min is still open by clicking on the application dock icon
 */
app.on('activate', function (/* e, hasVisibleWindows */) {
  if (!mainWindow && appIsReady) { // sometimes, the event will be triggered before the app is ready, and creating new windows will fail
    createWindow()
  }
})

ipc.on('focusMainWebContents', function () {
  mainWindow.webContents.focus()
})

ipc.on('showSecondaryMenu', function (event, data) {
  if (!secondaryMenu) {
    secondaryMenu = buildAppMenu({ secondary: true })
  }
  secondaryMenu.popup({
    x: data.x,
    y: data.y
  })
})

function registerProtocols () {
  protocol.registerStringProtocol('mailto', function (req, cb) {
    electron.shell.openExternal(req.url)
    return null
  })
}
;
var settings = {
  filePath: userDataPath + (process.platform === 'win32' ? '\\' : '/') + 'settings.json',
  list: {},
  onChangeCallbacks: [],
  save: function (cb) {
    fs.writeFile(settings.filePath, JSON.stringify(settings.list), function (e) {
      if (cb) {
        cb()
      }
    })
    // mainWindow.webContents.send('receiveSettingsData', settings.list)
  },
  runChangeCallacks () {
    // console.log("settings runChangeCallacks====")
    settings.onChangeCallbacks.forEach(function (listener) {
      if (listener.key) {
        listener.cb(settings.list[listener.key])
      } else {
        listener.cb()
      }
    })
  },
  get: function (key) {
    return settings.list[key]
  },
  listen: function (key, cb) {
    // console.log("settings listen==============")
    if (key && cb) {
      cb(settings.get(key))
      settings.onChangeCallbacks.push({key, cb})
    } else if (key) {
      // global listener
      settings.onChangeCallbacks.push({cb: key})
    }
  },
  set: function (key, value, cb) {
    settings.list[key] = value
    settings.save(cb)
    settings.runChangeCallacks()
  },
  initialize: function () {
    var fileData
    try {
      fileData = fs.readFileSync(settings.filePath, 'utf-8')
    } catch (e) {
      console.warn(e)
    }
    if (fileData) {
      settings.list = JSON.parse(fileData)
    }

    // ipc.on('receiveSettingsData', function (e, data) {
    //   settings.list = data
    //   settings.runChangeCallacks()
    // })
    ipc.on('getSettingsData', (event, arg) => {
	    event.returnValue = settings.list;
	})
    // ipc.on('getSettingsData', function() {
    //   var u1=tabs.get(tabId).url;
    //   if (!urlParser.isInternalURL(u1)) {
    //     throw new Error()
    //   }
    //   webviews.callAsync(tabId, 'send', ['receiveSettingsData', settings.list])
    // })
    ipc.on('setSetting', (event, arg) => {
      settings.set(arg.key, arg.value);
    })
  }
}

settings.initialize();
// module.exports = settings
// ;
var defaultFilteringSettings = {
  blockingLevel: 2,
  contentTypes: [],
  exceptionDomains: []
}

var enabledFilteringOptions = {
  blockingLevel: 0,
  contentTypes: [], // script, image
  exceptionDomains: []
}

// for tracking the number of blocked requests
var unsavedBlockedRequests = 0

setInterval(function () {
  if (unsavedBlockedRequests > 0) {
    var current = settings.get('filteringBlockedCount')
    if (!current) {
      current = 0
    }
    settings.set('filteringBlockedCount', current + unsavedBlockedRequests)
    unsavedBlockedRequests = 0
  }
}, 60000)

// electron uses different names for resource types than ABP
// electron: https://github.com/electron/electron/blob/34c4c8d5088fa183f56baea28809de6f2a427e02/shell/browser/net/atom_network_delegate.cc#L30
// abp: https://adblockplus.org/filter-cheatsheet#filter-options
var electronABPElementTypeMap = {
  mainFrame: 'document',
  subFrame: 'subdocument',
  stylesheet: 'stylesheet',
  script: 'script',
  image: 'image',
  object: 'object',
  xhr: 'xmlhttprequest',
  other: 'other' // ?
}

var parser = require('./ext/abp-filter-parser-modified/abp-filter-parser.js')
var parsedFilterData = {}

function initFilterList () {
  // console.log("initFilterList================= ")
  // discard old data if the list is being re-initialized
  parsedFilterData = {}

  fs.readFile(__dirname + '/ext/filterLists/easylist+easyprivacy-noelementhiding.txt', 'utf8', function (err, data) {
    if (err) {
      return
    }

    // data = data.replace(/.*##.+\n/g, '') // remove element hiding rules

    parser.parse(data, parsedFilterData)
    // console.log("read default")
    // console.log(parsedFilterData)
  })
  // console.log("read custom")
  fs.readFile(app.getPath('userData') + '/customFilters.txt', 'utf8', function (err, data) {
    if (!err && data) {
      parser.parse(data, parsedFilterData)
    }
    // console.log(parsedFilterData)
  })
}

function requestIsThirdParty (baseDomain, requestURL) {
  baseDomain = baseDomain.replace(/^www\./g, '')
  var requestDomain = parser.getUrlHost(requestURL).replace(/^www\./g, '')

  return !(parser.isSameOriginHost(baseDomain, requestDomain) || parser.isSameOriginHost(requestDomain, baseDomain))
}

function requestDomainIsException (domain) {
  if (domain.startsWith('www.')) {
    domain = domain.replace('www.', '')
  }
  return enabledFilteringOptions.exceptionDomains.includes(domain)
}

function handleRequest (details, callback) {
  // console.log("handleRequest===")
  // console.log(details.url)
  if (!(details.url.startsWith('http://') || details.url.startsWith('https://')) || details.resourceType === 'mainFrame') {
    // console.log("!http:// https:// cancel false")
    callback({
      cancel: false,
      requestHeaders: details.requestHeaders
    })
    return
  }

  // block javascript and images if needed

  if (enabledFilteringOptions.contentTypes.length > 0) {
    // console.log("enabledFilteringOptions.contentTypes.length > 0")
    for (var i = 0; i < enabledFilteringOptions.contentTypes.length; i++) {
      if (details.resourceType === enabledFilteringOptions.contentTypes[i]) {
        // console.log("cancel true")
        callback({
          cancel: true,
          requestHeaders: details.requestHeaders
        })
        return
      }
    }
  }

  if (details.webContentsId) {
    var domain = parser.getUrlHost(webContents.fromId(details.webContentsId).getURL())
  } else {
    // webContentsId may not exist if this request is for the main document of a subframe
    var domain = undefined
  }
  // console.log("domain")
  if (enabledFilteringOptions.blockingLevel > 0 && !(domain && requestDomainIsException(domain))) {
    if (
      (enabledFilteringOptions.blockingLevel === 1 && (!domain || requestIsThirdParty(domain, details.url))) ||
      (enabledFilteringOptions.blockingLevel === 2)
    ) {
      // console.log('enable filter')
      // by doing this check second, we can skip checking same-origin requests if only third-party blocking is enabled
      var matchesFilters = parser.matches(parsedFilterData, details.url, {
        domain: domain,
        elementType: electronABPElementTypeMap[details.resourceType]
      })
      if (matchesFilters) {
        // console.log("match")
        // console.log('cancel true')
        unsavedBlockedRequests++

        callback({
          cancel: true,
          requestHeaders: details.requestHeaders
        })
        return
      }
    }
  }
  // console.log("cancel false")
  callback({
    cancel: false,
    requestHeaders: details.requestHeaders
  })
}

function setFilteringSettings (settings) {
  // console.log("setFilteringSettings============")
  // console.log(settings)
  if (!settings) {
    settings = {}
  }

  for (var key in defaultFilteringSettings) {
    if (settings[key] === undefined) {
      settings[key] = defaultFilteringSettings[key]
    }
  }
  // console.log(settings)
  // console.log(enabledFilteringOptions)
  if (settings.blockingLevel > 0 && !(enabledFilteringOptions.blockingLevel > 0)) { // we're enabling tracker filtering
    initFilterList()
  }

  enabledFilteringOptions.contentTypes = settings.contentTypes
  enabledFilteringOptions.blockingLevel = settings.blockingLevel
  enabledFilteringOptions.exceptionDomains = settings.exceptionDomains.map(d => d.replace(/^www\./g, ''))
}

function registerFiltering (ses) {
  ses.webRequest.onBeforeRequest(handleRequest)
}

app.once('ready', function () {
  registerFiltering(session.defaultSession)
})

app.on('session-created', registerFiltering)

settings.listen('filtering', function (value) {
  // migrate from old settings (<v1.9.0)
  // console.log("listen filtering callback=============")
  // console.log(value)
  if (value && typeof value.trackers === 'boolean') {
    if (value.trackers === true) {
      value.blockingLevel = 2
    } else if (value.trackers === false) {
      value.blockingLevel = 0
    }
    delete value.trackers
    // settings.set('filtering', value)
  }

  setFilteringSettings(value)
});
// settings.set("filtering",{blockingLevel:2})//enabledFilteringOptions
var viewMap = {} // id: view
var viewStateMap = {} // id: view state

const BrowserView = electron.BrowserView

function createView (id, webPreferencesString, boundsString, events) {
  // console.log("createView==")
  // console.log(webPreferencesString);
  console.log(boundsString);
  // console.log(webPreferencesString);
  const view = new BrowserView(JSON.parse(webPreferencesString))

  events.forEach(function (event) {
    view.webContents.on(event, function (e) {
      /*
      new-window is special because its arguments contain a webContents object that can't be serialized and needs to be removed.
      */
      var args = Array.prototype.slice.call(arguments).slice(1)
      if (event === 'new-window') {
        e.preventDefault()
        args = args.slice(0, 3)
      }
      // console.log(args);
       if (mainWindow && mainWindow.webContents){
	      mainWindow.webContents.send('view-event', {
	        viewId: id,
	        event: event,
	        args: args
	      })
		}
    })
  })

  /*
  Workaround for crashes when calling preventDefault() on the new-window event (https://github.com/electron/electron/issues/23859#issuecomment-650270680)
  Calling preventDefault also prevents the new-window event from occurring, so create a new event here instead
  */
  view.webContents.on('-will-add-new-contents', function (e, url) {
    e.preventDefault()
    // console.log("-will-add-new-contents")
    // console.log(url);
    if (url.indexOf("about:blank")==-1)//not open blank window
    {
	    mainWindow.webContents.send('view-event', {
	      viewId: id,
	      event: 'new-window',
	      args: [url, '', 'new-window']
	    })
	}
  })

  view.webContents.on('ipc-message', function (e, channel, data) {
    mainWindow.webContents.send('view-ipc', {
      id: id,
      name: channel,
      data: data,
      frameId: e.frameId
    })
  })

  // Open a login prompt when site asks for http authentication
  view.webContents.on('login', (event, authenticationResponseDetails, authInfo, callback) => {
    if (authInfo.scheme !== 'basic') { // Only for basic auth
      return
    }
    event.preventDefault()
    var title = l('loginPromptTitle').replace('%h', authInfo.host).replace('%r', authInfo.realm)
    createPrompt({
      text: title,
      values: [{ placeholder: l('username'), id: 'username', type: 'text' },
        { placeholder: l('password'), id: 'password', type: 'password' }],
      ok: l('dialogConfirmButton'),
      cancel: l('dialogSkipButton'),
      width: 400,
      height: 200
    }, function (result) {
      // resend request with auth credentials
      callback(result.username, result.password)
    })
  })
  let bounds=JSON.parse(boundsString);
  
  view.setBounds(bounds)

  viewMap[id] = view
  viewStateMap[id] = { loadedInitialURL: false }

  return view
}

function destroyView (id) {
  if (!viewMap[id]) {
    return
  }

  if (viewMap[id] === mainWindow.getBrowserView()) {
    mainWindow.setBrowserView(null)
  }
  viewMap[id].webContents.destroy()

  delete viewMap[id]
  delete viewStateMap[id]
}

function destroyAllViews () {
  for (const id in viewMap) {
    destroyView(id)
  }
}

function setView (id) {
  mainWindow.setBrowserView(viewMap[id])
}

function setBounds (id, bounds) {
  if (isNaN(bounds.y) || bounds.y==null|| bounds.y<32){
  	console.log("bounds error y")
  	console.log(bounds)
  }
  if(isNaN(bounds.height) || bounds.height==null|| bounds.height<0){
  	console.log("bounds error height")
  	console.log(bounds)
  }
  if (viewMap[id]) {
  	if (isNaN(bounds.y)||isNaN(bounds.height)){
        ;
    }else{
    	viewMap[id].setBounds(bounds)
    }
  }
}

function focusView (id) {
  // empty views can't be focused because they won't propogate keyboard events correctly, see https://github.com/minbrowser/min/issues/616
  // also, make sure the view exists, since it might not if the app is shutting down
  if (viewMap[id] && viewMap[id].webContents && (viewMap[id].webContents.getURL() !== '' || viewMap[id].webContents.isLoading())) {
    viewMap[id].webContents.focus()
  } else if (mainWindow) {
    mainWindow.webContents.focus()
  }
}

function hideCurrentView () {
  mainWindow.setBrowserView(null)
  mainWindow.webContents.focus()
}

function getView (id) {
  return viewMap[id]
}

function getViewIDFromWebContents (contents) {
  for (var id in viewMap) {
    if (viewMap[id].webContents === contents) {
      return id
    }
  }
}

ipc.on('createView', function (e, args) {
  console.log("createView",args)
  createView(args.id, args.webPreferencesString, args.boundsString, args.events)
})

ipc.on('destroyView', function (e, id) {
  destroyView(id)
})

ipc.on('destroyAllViews', function () {
  destroyAllViews()
})

ipc.on('setView', function (e, args) {
  setView(args.id)
  setBounds(args.id, args.bounds)
  if (args.focus) {
    focusView(args.id)
  }
})

ipc.on('setBounds', function (e, args) {
	setBounds(args.id, args.bounds)
})

ipc.on('focusView', function (e, id) {
  focusView(id)
})

ipc.on('hideCurrentView', function (e) {
  hideCurrentView()
})

ipc.on('loadURLInView', function (e, args) {
  // wait until the first URL is loaded to set the background color so that new tabs can use a custom background
  // console.log('loadURLInView')
  // console.log(e);
  // console.log(args);
  if (!viewStateMap[args.id]) return;
  if (!viewStateMap[args.id].loadedInitialURL) {
    viewMap[args.id].setBackgroundColor('#fff')
  }
  viewMap[args.id].webContents.loadURL(args.url)
  viewStateMap[args.id].loadedInitialURL = true
})

ipc.on('callViewMethod', function (e, data) {
  console.log("callViewMethod");
  console.log(data);
  var error, result
  try {
    var webContents = viewMap[data.id].webContents
    var methodOrProp = webContents[data.method]
    // console.log(methodOrProp);
    if (methodOrProp instanceof Function) {
      // call function
      // console.log("call function")
      result = methodOrProp.apply(webContents, data.args)
    } else {
      // console.log("set property")
      // set property
      if (data.args && data.args.length > 0) {
        webContents[data.method] = data.args[0]
      }
      // read property
      result = methodOrProp
      
    }
    // console.log(result);
  } catch (e) {
    // console.log(e);
    error = e
  }
  if (result instanceof Promise) {
    result.then(function (result) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error: null, result })
      }
    })
    result.catch(function (error) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result: null })
      }
    })
  } else if (data.callId) {
    mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result })
  }
})

ipc.on('getCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
    return
  }

  view.webContents.capturePage().then(function (img) {
    var size = img.getSize()
    if (size.width === 0 && size.height === 0) {
      return
    }
    img = img.resize({ width: data.width, height: data.height })
    mainWindow.webContents.send('captureData', { id: data.id, url: img.toDataURL() })
  })
})

ipc.on('saveViewCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
  }

  view.webContents.capturePage().then(function (image) {
    view.webContents.downloadURL(image.toDataURL())
  })
})

global.getView = getView
;
const currrentDownloadItems = {}

ipc.on('cancelDownload', function (e, path) {
  if (currrentDownloadItems[path]) {
    currrentDownloadItems[path].cancel()
  }
})

function downloadHandler (event, item, webContents) {
  var itemURL = item.getURL()
    // send info to download manager
    sendIPCToWindow(mainWindow, 'download-info', {
      path: item.getSavePath(),
      name: item.getFilename(),
      status: 'progressing',
      size: { received: 0, total: item.getTotalBytes() }
    })

    item.on('updated', function (e, state) {
      if (item.getSavePath()) {
        currrentDownloadItems[item.getSavePath()] = item
      }
      sendIPCToWindow(mainWindow, 'download-info', {
        path: item.getSavePath(),
        name: item.getFilename(),
        status: state,
        size: { received: item.getReceivedBytes(), total: item.getTotalBytes() }
      })
    })

    item.once('done', function (e, state) {
      delete currrentDownloadItems[item.getSavePath()]
      sendIPCToWindow(mainWindow, 'download-info', {
        path: item.getSavePath(),
        name: item.getFilename(),
        status: state,
        size: { received: item.getTotalBytes(), total: item.getTotalBytes() }
      })
    })
  return true
}

// workaround for https://github.com/electron/electron/issues/24334
// function listenForPDFDownload (ses) {
//   ses.webRequest.onHeadersReceived(function (details, callback) {
//     if (details.resourceType === 'mainFrame' && details.responseHeaders) {
//       var typeHeader = details.responseHeaders[Object.keys(details.responseHeaders).filter(k => k.toLowerCase() === 'content-type')]
//       if (typeHeader instanceof Array && typeHeader.filter(t => t.includes('application/pdf')).length > 0 && details.url.indexOf('#pdfjs.action=download') === -1) {
//       // open in PDF viewer instead
//         callback({ cancel: true })
//         sendIPCToWindow(mainWindow, 'openPDF', {
//           url: details.url,
//           tabId: null
//         })
//         return
//       }
//     }
//     callback({ cancel: false })
//   })
// }

app.once('ready', function () {
  session.defaultSession.on('will-download', downloadHandler)
  // listenForPDFDownload(session.defaultSession)
})

app.on('session-created', function (session) {
  session.on('will-download', downloadHandler)
  // listenForPDFDownload(session)
})
;
/* Use the same user agent as Chrome to improve site compatibility and increase fingerprinting resistance
see https://github.com/minbrowser/min/issues/657 for more information */

let defaultUserAgent = app.userAgentFallback
let hasCustomUserAgent = false
let newUserAgent

if (settings.get('customUserAgent')) {
  newUserAgent = settings.get('customUserAgent')
  hasCustomUserAgent = true
} else {
  newUserAgent = defaultUserAgent.replace(/Min\/\S+\s/, '').replace(/Electron\/\S+\s/, '')
}
app.userAgentFallback = newUserAgent

/*
Google blocks signin in some cases unless a custom UA is used
see https://github.com/minbrowser/min/issues/868
*/
function enableGoogleUASwitcher (ses) {
  ses.webRequest.onBeforeSendHeaders((details, callback) => {
    if (!hasCustomUserAgent && details.url.includes('accounts.google.com')) {
      const url = new URL(details.url)

      if (url.hostname === 'accounts.google.com') {
        details.requestHeaders['User-Agent'] = newUserAgent + ' Edg/' + process.versions.chrome
      }
    }
    callback({ cancel: false, requestHeaders: details.requestHeaders })
  })
}

app.once('ready', function () {
  enableGoogleUASwitcher(session.defaultSession)
})

app.on('session-created', enableGoogleUASwitcher)
;
var pendingPermissions = []
var grantedPermissions = []
var nextPermissionId = 1

/*
All permission requests are given to the renderer on each change,
it will figure out what updates to make
*/
function sendPermissionsToRenderer () {
  // remove properties that can't be serialized over IPC
  sendIPCToWindow(mainWindow, 'updatePermissions', pendingPermissions.concat(grantedPermissions).map(p => {
    return {
      permissionId: p.permissionId,
      tabId: p.tabId,
      permission: p.permission,
      details: p.details,
      granted: p.granted
    }
  }))
}

function removePermissionsForContents (contents) {
  pendingPermissions = pendingPermissions.filter(perm => perm.contents !== contents)
  grantedPermissions = grantedPermissions.filter(perm => perm.contents !== contents)

  sendPermissionsToRenderer()
}

/*
Was permission already granted for this tab and URL?
*/
function isPermissionGrantedForContents (requestContents, requestPermission, requestDetails) {
  var requestOrigin = new URL(requestDetails.requestingUrl).hostname

  for (var i = 0; i < grantedPermissions.length; i++) {
    var grantedOrigin = new URL(grantedPermissions[i].details.requestingUrl).hostname

    if (requestContents === grantedPermissions[i].contents && requestOrigin === grantedOrigin) {
      if (requestPermission === 'notifications' && grantedPermissions[i].permission === 'notifications') {
        return true
      }

      if (requestPermission === 'media' && grantedPermissions[i].permission === 'media') {
        // type 1: from permissionCheckHandler
        // request has a single media type
        if (requestDetails.mediaType && grantedPermissions[i].details.mediaTypes.includes(requestDetails.mediaType)) {
          return true
        }
        // type 2: from a permissionRequestHandler
        // request has multiple media types
        // TODO existing granted permissions should be merged together (i.e. if there is an existing permission for audio, and another for video, a new request for audio+video should be approved, but it currently won't be)
        if (requestDetails.mediaTypes && requestDetails.mediaTypes.every(type => grantedPermissions[i].details.mediaTypes.includes(type))) {
          return true
        }
      }
    }
  }
  return false
}

/*
Is there already a pending request of the given type for this tab+url?
 */
function hasPendingRequestForContents (contents, permission, details) {
  var requestOrigin = new URL(details.requestingUrl).hostname

  for (var i = 0; i < pendingPermissions.length; i++) {
    var pendingOrigin = new URL(pendingPermissions[i].details.requestingUrl).hostname

    if (contents === pendingPermissions[i].contents && requestOrigin === pendingOrigin && permission === pendingPermissions[i].permission) {
      return true
    }
  }
  return false
}

function pagePermissionRequestHandler (webContents, permission, callback, details) {
  if (!details.isMainFrame) {
    // not supported for now to simplify the UI
    callback(false)
    return
  }

  if (permission === 'fullscreen') {
    callback(true)
    return
  }

  /*
  Geolocation requires a Google API key (https://www.electronjs.org/docs/api/environment-variables#google_api_key), so it is disabled.
  Other permissions aren't supported for now to simplify the UI
  */
  if (['media', 'notifications'].includes(permission)) {
    /*
    If permission was previously granted for this page, new requests should be allowed
    */
    if (isPermissionGrantedForContents(webContents, permission, details)) {
      callback(true)
    } else if (permission === 'notifications' && hasPendingRequestForContents(webContents, permission, details)) {
      /*
      Sites sometimes make a new request for each notification, which can generate multiple requests if the first one wasn't approved.
      TODO this isn't entirely correct (some requests will be rejected when they should be pending) - correct solution is to show a single button to approve all requests in the UI.
      */
      callback(false)
    } else {
      pendingPermissions.push({
        permissionId: nextPermissionId,
        tabId: getViewIDFromWebContents(webContents),
        contents: webContents,
        permission: permission,
        details: details,
        callback: callback
      })

      sendPermissionsToRenderer()

      nextPermissionId++
    }

    /*
    Once this view is closed or navigated to a new page, these permissions should be revoked
    */
    webContents.on('did-start-navigation', function (e, url, isInPlace, isMainFrame, frameProcessId, frameRoutingId) {
      if (isMainFrame && !isInPlace) {
        removePermissionsForContents(webContents)
      }
    })
    webContents.once('destroyed', function () {
      // check whether the app is shutting down to avoid an electron crash (TODO remove this)
      if (mainWindow) {
        removePermissionsForContents(webContents)
      }
    })
  } else {
    callback(false)
  }
}

function pagePermissionCheckHandler (webContents, permission, requestingOrigin, details) {
  if (!details.isMainFrame) {
    return false
  }

  return isPermissionGrantedForContents(webContents, permission, details)
}

app.once('ready', function () {
  session.defaultSession.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.defaultSession.setPermissionCheckHandler(pagePermissionCheckHandler)
})

app.on('session-created', function (session) {
  session.setPermissionRequestHandler(pagePermissionRequestHandler)
  session.setPermissionCheckHandler(pagePermissionCheckHandler)
})

ipc.on('permissionGranted', function (e, permissionId) {
  for (var i = 0; i < pendingPermissions.length; i++) {
    if (permissionId && pendingPermissions[i].permissionId === permissionId) {
      pendingPermissions[i].granted = true
      pendingPermissions[i].callback(true)
      grantedPermissions.push(pendingPermissions[i])
      pendingPermissions.splice(i, 1)

      sendPermissionsToRenderer()
      break
    }
  }
})
;
/* Simple input prompt. */

var promptAnswer
var promptOptions

function createPrompt (options, callback) {
  promptOptions = options
  let { parent, width = 360, height = 140 } = options

  var promptWindow = new BrowserWindow({
    width: width,
    height: height,
    parent: parent != null ? parent : mainWindow,
    show: false,
    modal: true,
    alwaysOnTop : true,
    title : options.title,
    // autoHideMenuBar: true,
    frame: false,
    webPreferences: {
      nodeIntegration: true,
      sandbox: false
    }
  })

  promptWindow.on('closed', () => {
    promptWindow = null
    callback(promptAnswer)
  })

  // Load the HTML dialog box
  promptWindow.loadURL('file://' + __dirname + '/pages/prompt/index.html')
  promptWindow.once('ready-to-show', () => { promptWindow.show() })
}

ipc.on('show-prompt', function (options, callback) {
  createPrompt(options, callback)
})

ipc.on('open-prompt', function (event) {
  event.returnValue = JSON.stringify({
    label: promptOptions.text,
    ok: promptOptions.ok,
    values: promptOptions.values,
    cancel: promptOptions.cancel,
    darkMode: settings.get('darkMode')
  })
})

ipc.on('close-prompt', function (event, data) {
  promptAnswer = data
})

ipc.on('prompt', function (event, data) {
  createPrompt(data, function (result) {
    event.returnValue = result
  })
})
;
ipc.on('open-context-menu', function (e, data) {
  var menu = new Menu()

  data.template.forEach(function (section) {
    section.forEach(function (item) {
      var id = item.click
      item.click = function () {
        e.sender.send('context-menu-item-selected', { menuId: data.id, itemId: id })
      }
      menu.append(new MenuItem(item))
    })
    menu.append(new MenuItem({ type: 'separator' }))
  })
  menu.on('menu-will-close', function () {
    e.sender.send('context-menu-will-close', {menuId: data.id})
  })
  menu.popup({ x: data.x, y: data.y })
})
;
/*
Wrapper for node-keytar
Runs in the main process because of https://github.com/atom/node-keytar/issues/250
*/
pwd.init();
ipc.handle('keychainGetPassword', async function (event, service, account) {
  return await pwd.selectPassword(service, account)
})

ipc.handle('keychainSetPassword', async function (event, service, account, password) {
  return await pwd.setPassword(service, account, password)
})

ipc.handle('keychainDeletePassword', async function (event, service, account) {
  return await pwd.deletePassword(service, account)
})

ipc.handle('keychainFindCredentials', async function (event, service) {
	// console.log("keychainFindCredentials")
  	return pwd.selectAccount(service)
})
// ipc.handle('keychainFindCredentials', function (event, service) {
// 	console.log("keychainFindCredentials")
// 	return ["a","b","c"]
// })

ipc.handle('keychainFindPassword', function (event, service) {
  // return keytar.setPassword(service)
})
ipc.on('close', (event, arg) => {
  // console.log("ipcMain on close");
  safeExit=true;
  app.quit();
})
let proxyConfig = {}

settings.listen('proxy', (proxy = {}) => {
  switch (proxy.type) {
    case 1:
      proxyConfig = {
        pacScript: '',
        proxyRules: proxy.proxyRules,
        proxyBypassRules: proxy.proxyBypassRules
      }
      break
    case 2:
      proxyConfig.pacScript = proxy.pacScript
      break
    default:
      proxyConfig = {}
  }

  webContents.getAllWebContents().forEach(wc => wc.session && wc.session.setProxy(proxyConfig))
})

app.on('session-created', session => session.setProxy(proxyConfig))
;

